package com.octopus.kernal.service;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import com.octopus.kernal.model.BaseStruct;
import com.octopus.kernal.util.xml.BusinessModel;
import com.octopus.kernal.util.xml.Entity;

@Service
public class BusinessModelService {
	
	private BusinessModel businessModel;
	
	private Map<String, BusinessRuleInterface> businessRules;
	
	private Map<String, BusinessScopeInterface> businessScopes;
	
	public BusinessModelService() throws JAXBException {
		JAXBContext context = JAXBContext.newInstance(BusinessModel.class);
		Unmarshaller unmarshal = context.createUnmarshaller();
		InputStream is = getClass().getResourceAsStream("/BusinessModel.xml");
		BusinessModel model = (BusinessModel)unmarshal.unmarshal(new InputStreamReader(is));
		this.businessModel = model;
		
		businessRules = new HashMap<String, BusinessRuleInterface>();
		businessScopes = new HashMap<String, BusinessScopeInterface>();
		for(Entity entity : model.getEntities()){
			if(entity.getActions() == null)
				continue;
			for(com.octopus.kernal.util.xml.Field field : entity.getFields()){
				if(field.getElementDetail() != null && field.getElementDetail().getSelectElement() != null){
					String scope = field.getElementDetail().getSelectElement().getScope();
					if(StringUtils.isBlank(scope))
						continue;
					try {
						Class<?> clazz = Class.forName(scope);
						if(!businessScopes.containsKey(scope))
							businessScopes.put(scope, (BusinessScopeInterface)clazz.newInstance());
					} catch (Exception e) {
					}
				}
				if(field.getElementSearch() != null && field.getElementSearch().getSelectElement() != null){
					String scope = field.getElementSearch().getSelectElement().getScope();
					if(StringUtils.isBlank(scope))
						continue;
					try {
						Class<?> clazz = Class.forName(scope);
						if(!businessScopes.containsKey(scope))
							businessScopes.put(scope, (BusinessScopeInterface)clazz.newInstance());
					} catch (Exception e) {
					}
				}
			}
			for(com.octopus.kernal.util.xml.Action action : entity.getActions()){
				String rule = action.getRule();
				if(StringUtils.isBlank(rule))
					continue;
				try {
					Class<?> clazz = Class.forName(rule);
					if(!businessRules.containsKey(rule))
						businessRules.put(rule, (BusinessRuleInterface)clazz.newInstance());
				} catch (Exception e) {
				}
			}
		}
	}
	
	public List<Entity> getEntities(){
		return this.businessModel.getEntities();
	}
	
	public Entity getEntityByName(String name){
		List<Entity> list = this.getEntities();
		if(list == null)
			return null;
		for(Entity entity : list){
			if(entity.getName().equals(name))
				return entity;
		}
		return null;
	}
	
	public Entity getEntityByClass(Class<?> clazz){
		String name = clazz.getSimpleName();
		return this.getEntityByName(name.substring(0, name.indexOf("Struct")));
	}
	
	@SuppressWarnings("unchecked")
	public Class<BaseStruct> getReferenceStruct(BaseStruct struct, String fieldName){
		try {
			Field field = struct.getClass().getDeclaredField(fieldName);
			Type t = field.getGenericType();
			if (t instanceof ParameterizedType) {
				Type[] p = ((ParameterizedType) t).getActualTypeArguments();
				Type type = p[0];
				return (Class<BaseStruct>)type;
			}else{
				return (Class<BaseStruct>)t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public boolean isActionAllowed(BaseStruct struct, String ruleClass){
		if(StringUtils.isBlank(ruleClass))
			return true;
		BusinessRuleInterface rule = businessRules.get(ruleClass);
		if(rule == null)
			return true;
		return rule.isAllowed(struct);
	}
	
	public List<Object> getScope(String scopeClass){
		if(StringUtils.isBlank(scopeClass))
			return new ArrayList<Object>();
		BusinessScopeInterface scope = businessScopes.get(scopeClass);
		return scope.getScope();
	}

}
